/// @brief 提供一个线程安全的队列，用于同步数据
///        生产者线程使用push()方法将数据放入队列，消费者线程使用pop()方法从队列中获取数据
///        队列的大小由DATA_COUNT参数指定，如果DATA_COUNT为0，则不限制队列大小
///        如果队列满了，生产者线程会阻塞，直到消费者线程从队列中取出数据
///        如果队列为空，消费者线程会阻塞，直到生产者线程将数据放入队列

#ifndef __SYNC_QUEUE_H__
#define __SYNC_QUEUE_H__

#include "../utools_cfg.h"

#if UTOOLS_COLLAB_SYNC_QUEUE_ENABLE

#include <queue>
#include <functional>
#include <type_traits>
#include <cstddef>
#include "utcondition_variable.h"
#include "utmutex.h"

#if __cplusplus >= 201703L
#include <optional>
#endif

namespace utools::collab
{
    /// @brief 同步队列
    /// @tparam _SYNC_DATA_TYPE 对列中的数据类型
    /// @tparam DATA_COUNT 最大队列大小，如果为0，则不限制队列大小
    template <typename _SYNC_DATA_TYPE, size_t DATA_COUNT = 20>
    class SyncQueue
    {
    public:
        using size_type = typename std::queue<_SYNC_DATA_TYPE>::size_type;
        using value_type = typename std::queue<_SYNC_DATA_TYPE>::value_type;
        using reference = typename std::queue<_SYNC_DATA_TYPE>::reference;
        using const_reference = typename std::queue<_SYNC_DATA_TYPE>::const_reference;

        SyncQueue() = default;
        virtual ~SyncQueue() {}

        /// @brief 向队列中增加一个数据（兼容原接口）
        /// @param data 要增加的数据
        /// @return true 成功，false 队列已释放
        /// @note 为保持兼容性，恢复原有的丢弃行为，但返回状态，不推荐使用，请使用 push_wait()
        template <typename T>
        bool push(T &&data)
        {
            ::utools::collab::UniqueLock lock(__mutex);
            if (__released)
                return false;

            if constexpr (DATA_COUNT > 0)
            {
                // 为保持原有兼容性，仍然丢弃旧数据
                while (__queue.size() >= DATA_COUNT)
                {
                    __queue.pop();
                }
            }
            __queue.push(std::forward<T>(data));
            __cv.notify_one();
            return true;
        }

        /// @brief 向队列中增加一个数据，会阻塞等待
        /// @param data 要增加的数据
        template <typename T>
        void push_wait(T &&data)
        {
            ::utools::collab::UniqueLock lock(__mutex);
            if constexpr (DATA_COUNT > 0)
            {
                while (__queue.size() >= DATA_COUNT && !__released)
                {
                    __cv.wait(lock); // 等待消费者取走数据
                }
                if (__released)
                    return; // 如果已释放则退出
            }
            __queue.push(std::forward<T>(data));
            __cv.notify_one();
        }

        /// @brief 向队列中增加一个数据，会直接调用构造函数
        /// @param args 要增加的数据的变量
        template <typename... Args>
        void emplace(Args &&...args)
        {
            ::utools::collab::UniqueLock lock(__mutex);
            if constexpr (DATA_COUNT > 0)
            {
                while (__queue.size() >= DATA_COUNT)
                {
                    __queue.pop();
                }
            }
            __queue.emplace(std::forward<Args>(args)...);
            __cv.notify_one();
        }

#if __cplusplus >= 201703L
        /// @brief 等待数据并弹出，会返回数据
        /// @return 头部数据或空std::nullopt
        /// @note 如果接收到__released信号，会直接返回std::nullopt
        std::optional<value_type> pop_wait_safety()
        {
            ::utools::collab::UniqueLock lock(__mutex);
            while (__queue.empty() && !__released)
            {
                __cv.wait(lock);
            }
            if (__released && __queue.empty())
            {
                return std::nullopt;
            }
            auto data{std::move(__queue.front())};
            __queue.pop();
            return data;
        }
#endif

        /// @brief 等待数据弹出，会返回数据
        /// @return 头部数据
        /// @warning 如果接收到__released信号，会优化判断是否可以返回nullptr数据
        /// @warning 如果不行会直接返回数据类型对应的默认构造值，这可能是不安全的
        value_type pop_wait()
        {
            ::utools::collab::UniqueLock lock(__mutex);
            while (__queue.empty() && !__released)
            {
                __cv.wait(lock);
            }
            if (__released)
            {
                if constexpr (std::is_pointer<value_type>::value ||
                              std::is_convertible<value_type, std::nullptr_t>::value)
                {
                    return nullptr;
                }
                else
                {
                    return value_type{};
                }
            }
            auto data{std::move(__queue.front())};
            __queue.pop();
            return data;
        }

        /// @brief 等待数据弹出，会返回数据
        /// @param data 返回的数据
        /// @warning 如果接收到__released信号，会优化判断是否可以返回nullptr数据
        /// @warning 如果不行会直接返回数据类型对应的默认构造值，这可能是不安全的
        void pop_wait(value_type &data)
        {
            ::utools::collab::UniqueLock lock(__mutex);
            while (__queue.empty() && !__released)
            {
                __cv.wait(lock);
            }
            if (__released)
            {
                if constexpr (std::is_pointer<value_type>::value ||
                              std::is_convertible<value_type, std::nullptr_t>::value)
                {
                    data = nullptr;
                }
                else
                {
                    data = value_type{};
                }
            }
            else
            {
                data = std::move(__queue.front());
                __queue.pop();
            }
        }

        /// @brief 等待数据非空时
        /// @return true: 队列非空，false: 队列已释放
        bool wait_not_empty()
        {
            ::utools::collab::UniqueLock lock(__mutex);
            while (__queue.empty() && !__released)
            {
                __cv.wait(lock);
            }
            return !__released;
        }

        /// @brief 安全的弹出数据，不会返回任何数据
        /// @return true 成功弹出，false 队列为空
        bool pop()
        {
            ::utools::collab::UniqueLock lock(__mutex);
            if (__queue.empty())
            {
                return false; // 队列为空，安全返回
            }
            __queue.pop();
            return true;
        }

        /// @brief 安全获取队列头部数据
        /// @param result 输出参数，存储头部数据
        /// @return true 成功获取，false 队列为空
        bool front(value_type &result) const
        {
            ::utools::collab::UniqueLock lock(__mutex);
            if (__queue.empty())
            {
                return false;
            }
            result = __queue.front();
            return true;
        }

        /// @brief 安全获取队列尾部数据
        /// @param result 输出参数，存储尾部数据
        /// @return true 成功获取，false 队列为空
        bool back(value_type &result) const
        {
            ::utools::collab::UniqueLock lock(__mutex);
            if (__queue.empty())
            {
                return false;
            }
            result = __queue.back();
            return true;
        }

        size_type size()
        {
            ::utools::collab::UniqueLock lock(__mutex);
            return __queue.size();
        }

        bool empty()
        {
            ::utools::collab::UniqueLock lock(__mutex);
            return __queue.empty();
        }

        void clear()
        {
            ::utools::collab::UniqueLock lock(__mutex);
            while (!__queue.empty())
            {
                __queue.pop();
            }
        }

        /// @brief 发送通知释放当前的pop()操作
        /// @warning 如不了解工作过程，请不要手动调用该函数
        void notify_one()
        {
            __cv.notify_one();
        }

        /// @brief 发送通知释放当前的pop()操作
        /// @warning 如不了解工作过程，请不要手动调用该函数
        void notify_all()
        {
            __cv.notify_all();
        }

        void release()
        {
            __released = true;
            notify_all();
        }

        /// @brief 重新初始化队列
        void reset()
        {
            ::utools::collab::UniqueLock lock(__mutex);
            __released = true;
            __cv.notify_all(); // 在锁保护下通知

            // 清空队列
            while (!__queue.empty())
            {
                __queue.pop();
            }
            __released = false;
        }

        /// @brief 获取队列数据副本
        /// @return 返回队列数据的副本
        std::queue<_SYNC_DATA_TYPE> get_data_copy() const
        {
            ::utools::collab::UniqueLock lock(__mutex);
            return __queue; // 返回副本
        }

        /// @brief 获取队列大小和是否为空的状态
        /// @param out_size 输出队列大小
        /// @param out_empty 输出是否为空
        void get_status(size_type &out_size, bool &out_empty) const
        {
            ::utools::collab::UniqueLock lock(__mutex);
            out_size = __queue.size();
            out_empty = __queue.empty();
        }

    private:
        mutable ::utools::collab::Mutex __mutex;
        ::utools::collab::ConditionVariable __cv;
        std::queue<_SYNC_DATA_TYPE> __queue;
        bool __released{false};
    };

    template <typename _SYNC_DATA_TYPE, size_t DATA_COUNT = 20>
    SyncQueue<_SYNC_DATA_TYPE, DATA_COUNT> make_sync_queue()
    {
        return SyncQueue<_SYNC_DATA_TYPE, DATA_COUNT>();
    }
}

#endif // UTOOLS_COLLAB_SYNC_QUEUE_ENABLE
#endif // __SYNC_QUEUE_H__
